package com.linkdood.app.component;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.quartz.*;
import org.quartz.impl.JobDetailImpl;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.stereotype.Component;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.Set;

/**
 * 提供 Quartz Job Trigger JobDetail 基础方法
 *
 * @author hycao
 */
@Slf4j
@Component
public class QuartzJobComponent {

    /**
     * 单例模式调度器
     */
    private final Scheduler scheduler;

    /**
     * CronTrigger的工厂类
     */
    private final CronTriggerFactoryBean triggerFactory;
    /**
     * JobDetail的工厂类
     */
    private final JobDetailFactoryBean jobDetailFactory;

    private final SimpleSchedulerListener simpleSchedulerListener;

    public QuartzJobComponent(Scheduler scheduler, SimpleSchedulerListener simpleSchedulerListener) {
        this.scheduler = scheduler;
        this.simpleSchedulerListener = simpleSchedulerListener;
        triggerFactory = new CronTriggerFactoryBean();
        jobDetailFactory = new JobDetailFactoryBean();
    }


    /**
     * 获取 jobkey
     *
     * @param name 任务名称
     */
    public JobKey getJobKey(String name, String group) {
        if (StringUtils.isNotBlank(group)) {
            group = "Default group";
        }
        return JobKey.jobKey(name, group);
    }

    /**
     * 获取 triggerKey
     *
     * @param name 任务名称
     */
    public TriggerKey getTriggerKey(String name, String groupName) {
        if (StringUtils.isNotBlank(groupName)) {
            groupName = "Default group";
        }
        return TriggerKey.triggerKey(name, groupName);
    }

    /**
     * 获取表达式触发器
     *
     * @param name      任务名称
     * @param groupName 组名
     */
    public CronTrigger getCronTrigger(String name, String groupName) throws SchedulerException {
        return (CronTrigger) scheduler.getTrigger(getTriggerKey(name, groupName));
    }

    /**
     * 暂停任务
     *
     * @param name 任务名称
     */
    public void pauseJob(String name, String group) {
        log.debug("List all jobs detail：{}", getAllJobs().toString());
        try {
            scheduler.pauseJob(getJobKey(name, group));
            log.debug("List all jobs detail：{}", getAllJobs().toString());
            scheduler.pauseTrigger(getTriggerKey(name, group));

        } catch (SchedulerException e) {
            log.error("Pause job error : {}", e.getMessage());
        }
        log.debug("List all jobs detail：{}", getAllJobs().toString());
    }

    /**
     * 恢复任务
     *
     * @param name 任务名称
     */
    public void resumeJob(String name, String group) {
        log.debug("List all jobs detail：{}", getAllJobs().toString());
        try {
            scheduler.resumeJob(getJobKey(name, group));
        } catch (SchedulerException e) {
            log.error("Resume job error : {}", e.getMessage());
        }
        log.debug("List all jobs detail：{}", getAllJobs().toString());
    }

    /**
     * 创建任务
     *
     * @param jobDetail 任务详情
     * @param trigger   触发器
     */
    public Date create(JobDetail jobDetail, Trigger trigger) throws SchedulerException {
        Date date = scheduler.scheduleJob(jobDetail, trigger);
        if (!scheduler.isShutdown()) {
            scheduler.start();
        }

        // 添加监听器
        ListenerManager listenerManager = scheduler.getListenerManager();
        List<SchedulerListener> schedulerListeners = listenerManager.getSchedulerListeners();
        if (schedulerListeners.isEmpty()) {
            scheduler.getListenerManager().addSchedulerListener(simpleSchedulerListener);
        }

        return date;
    }

    /**
     * 删除任务
     *
     * @param name      旧任务名称
     * @param groupName 旧组名
     */
    public Boolean delete(String name, String groupName) throws SchedulerException {
        log.debug("List all jobs detail：{}", getAllJobs().toString());
        TriggerKey key = getTriggerKey(name, groupName);
        //根据TriggerKey获取trigger是否存在，如果存在则根据key进行删除操作
        Trigger keyTrigger = scheduler.getTrigger(key);
        if (keyTrigger != null) {
            scheduler.unscheduleJob(key);
        }
        log.debug("List all jobs detail：{}", getAllJobs().toString());
        return true;
    }

    /**
     * 更新任务
     *
     * @param name    任务名称
     * @param group   任务组名
     * @param trigger 触发器
     */
    public Optional<Date> reschedule(String name, String group, Trigger trigger) throws SchedulerException {
        List<? extends Trigger> triggersOfJob = scheduler.getTriggersOfJob(getJobKey(name, group));
        if (triggersOfJob.isEmpty()) {
            return Optional.empty();
        }

        // 如果存在多个，理论上不存在，则返回异常
        if (triggersOfJob.size() > 1) {
            throw new SchedulerException("Expected reschedule one job, but found:" + triggersOfJob.size());
        }

        Date date = scheduler.rescheduleJob(triggersOfJob.get(0).getKey(), trigger);
        return Optional.ofNullable(date);
    }


    /**
     * 根据指定的参数，创建JobDetail
     *
     * @param clazz       定时任务类
     * @param name        名称
     * @param groupName   组名
     * @param description 描述
     */
    public JobDetail createJobDetail(Class<? extends Job> clazz, String name, String groupName, String description) {
        jobDetailFactory.setJobClass(clazz);
        jobDetailFactory.setName(name);
        jobDetailFactory.setGroup(groupName);
        jobDetailFactory.setDescription(description);
        jobDetailFactory.setDurability(true);
        jobDetailFactory.afterPropertiesSet();
        return jobDetailFactory.getObject();
    }

    /**
     * 根据参数，创建对应的CronTrigger对象
     *
     * @param job         任务详情
     * @param time        时间
     * @param name        任务名称
     * @param groupName   组名
     * @param description 描述
     */
    public CronTrigger createCronTrigger(JobDetail job, String time, String name, String groupName, String description) {
        triggerFactory.setName(name);
        triggerFactory.setJobDetail(job);
        triggerFactory.setCronExpression(time);
        triggerFactory.setDescription(description);
        triggerFactory.setGroup(groupName);
        try {
            triggerFactory.afterPropertiesSet();
        } catch (ParseException e) {
            log.error("Create cron trigger error : {}", e.getMessage());
        }
        return triggerFactory.getObject();
    }

    /**
     * 获取当前调度信息
     */
    public List<QuartzJobsVO> getAllJobs() {
        List<QuartzJobsVO> quartzJobsVOList = new ArrayList<>();
        try {
            // 获取Scheduler下的所有group
            List<String> triggerGroupNames = scheduler.getTriggerGroupNames();
            for (String groupName : triggerGroupNames) {
                // 组装group的匹配，为了模糊获取所有的triggerKey或者jobKey
                GroupMatcher<TriggerKey> groupMatcher = GroupMatcher.groupEquals(groupName);
                // 获取所有的triggerKey
                Set<TriggerKey> triggerKeySet = scheduler.getTriggerKeys(groupMatcher);
                for (TriggerKey triggerKey : triggerKeySet) {
                    // 通过triggerKey在scheduler中获取trigger对象
                    CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
                    // 获取trigger拥有的Job
                    JobKey jobKey = trigger.getJobKey();
                    JobDetailImpl jobDetail = (JobDetailImpl) scheduler.getJobDetail(jobKey);
                    // 获取当前任务状态
                    Trigger.TriggerState triggerState = scheduler.getTriggerState(triggerKey);

                    // 组装页面需要显示的数据
                    QuartzJobsVO quartzJobsVO = new QuartzJobsVO();
                    quartzJobsVO.setGroupName(groupName);
                    quartzJobsVO.setJobDetailName(jobDetail.getName());
                    quartzJobsVO.setJobCronExpression(trigger.getCronExpression());
                    quartzJobsVO.setTimeZone(trigger.getTimeZone().getID());
                    quartzJobsVO.setTriggerState(triggerState);
                    quartzJobsVOList.add(quartzJobsVO);
                }
            }
        } catch (Exception e) {
            log.error("List all jobs detail error: {}", e.getMessage());
        }
        return quartzJobsVOList;
    }

    /**
     * Quartz 任务查询类
     *
     * @author hycao
     */
    @Data
    private static class QuartzJobsVO {

        private String jobDetailName;

        private String jobCronExpression;

        private String timeZone;

        private String groupName;

        Trigger.TriggerState triggerState;
    }
}
